home *** CD-ROM | disk | FTP | other *** search
- /* Revision History:
-
- 94/01/09 aih
- - changed how background notification options are set so they
- can be loaded from a resource
-
- 93/03/25 AIH
- - Checks for Notification Manager before posting a notification
-
- 91/11/14 AIH
- - Uses the operating system utilities to capitalize the alert's control title
- to check for equivalence with a command-key (instead of using toupper()).
-
- 91/07/03 AIH
- - Added a postcondition to one of the functions
-
- 91/06/13 AIH
- - Removed definitions of low-memory globals since they're already
- defined in a more central file
- - Fixed bug which caused the sound routine to be ignored if the
- dialog was invisible
- - Clarified a few comments
-
- 91/05/10 AIH
- - Uses an 'ALRT' resource and handles the stage list in much the same way
- as the standard alert function, as described in IM-1
- - Adapted to work with Apple Event manager -- calls AEInteractWithUser
- (via the event library) if an Apple Event is being executed
-
- 91/05/08 AIH
- - Adapted to return error codes if the alert couldn't be created
-
- 91/04/24 AIH
- - Added user supplied filter
-
- 91/04/22 AIH
- - Modified the names of a few functions and constants
-
- 91/04/17 AIH
- - The filter function saves and restores the current port
-
- 91/04/03 AIH
- - Icon is drawn on update events, instead of using a user item
-
- 91/03/25 AIH
- - Adapted for new way of using event library
-
- 91/03/23 AIH
- - The "posted notification" flag wasn't getting reset when the notification
- was removed
-
- 91/03/19 AIH
- - Added function for setting background notification options
-
- 91/03/10-12 AIH
- - Uses the window library to determine the kind of window
- - String for alert is passed as a parameter, and set in first static
- text item
- - Icon is set in first user item
- - Command key equivalents of buttons are supported
-
- 91/03/07 AIH
- - A filter procedure redraws the alert on update events
- - Alerts are now really dialogs
-
- 91/01/21 AIH
- - Got rid of method of setting the titles of buttons in an alert.
- This didn't work well, caused annoying flicker, and was complicated.
- Also simplified the library.
- - Added use of DlgErrSetup
-
- 91/01/05 Ari Halberstadt
- - Inserted this standard header in all files */
-
- #include <stdarg.h>
- #include <string.h>
- #include "AlertLib.h"
- #include "EventLib.h"
- #include "GlobalLib.h"
- #include "LowMemLib.h"
- #include "MacLib.h"
- #include "MemoryLib.h"
- #include "MenuLib.h"
- #include "ResourceLib.h"
- #include "StringLib.h"
- #include "WindowLib.h"
-
- static AlertOptionsType gAlertOptions;
-
- /* set background notification options */
- void AlertOptionsSet(const AlertOptionsType *options)
- {
- gAlertOptions = *options;
- }
-
- /* If we're in the background, then post a notification and enter a
- nested event loop. When the application is brought to the foreground
- the nested event loop is exited. Since the event loop is recursive,
- and since the only static variable is a flag indicating that we've already
- posted a notification, it is possible for the application to queue any
- number of alerts while it is in the background. The alerts will be displayed
- in the reverse of the order they were posted (i.e., last alert to be posted
- appears first) and only one alert will actually be visible on the screen at
- a time. All of this is done according to the HIG, which specify that alerts
- shouldn't appear while an application is in the background and that
- multiple background notifications should be avoided. */
- static void NotifyIfInBackground(void)
- {
- static Boolean posted; /* true if a notification has been posted */
- static NMRec notification; /* the notification */
- CStr255 string; /* string to display in notification */
- volatile Handle sicn = NULL; /* small icon to display in menu bar */
- volatile Handle sound = NULL; /* sound to play */
- volatile SignedByte soundState = 0;/* saved state of handle to sound resource */
- volatile SignedByte sicnState = 0; /* saved state of handle to small icon resource */
-
- TRY {
- if (MacHasNotificationMgr() &&
- EventInBackground() &&
- gAlertOptions.flags != ALERT_NOTIFY_NONE)
- {
- if (! posted) {
- /* configure notification according to options */
- memclr(¬ification, sizeof(NMRec));
- notification.qType = nmType;
-
- /* set mark character */
- if ((gAlertOptions.flags & ALERT_NOTIFY_MARK) != 0)
- notification.nmMark = gAlertOptions.mark;
-
- /* set small icon */
- sicn = NULL;
- if ((gAlertOptions.flags & ALERT_NOTIFY_ICON) != 0) {
- sicn = ResGet('SICN', gAlertOptions.sicn);
- sicnState = HandleNoPurge(sicn);
- notification.nmIcon = sicn;
- }
-
- /* set alert string */
- if ((gAlertOptions.flags & ALERT_NOTIFY_ALERT) != 0) {
- ResString(gAlertOptions.string, string);
- if (*string)
- notification.nmStr = (StringPtr) c2pstr(string);
- }
-
- /* set sound */
- sound = NULL;
- if ((gAlertOptions.flags & ALERT_NOTIFY_SOUND) != 0) {
- if (gAlertOptions.sound == -1)
- notification.nmSound = (Handle) -1L;
- else {
- sound = ResGet('snd ', gAlertOptions.sound);
- soundState = HandleNoPurge(sound);
- notification.nmSound = sound;
- }
- }
-
- /* post notification */
- FailOSErr(NMInstall(¬ification));
- posted = true;
- }
-
- /* handle events till the application is brought to the foreground */
- while (EventInBackground())
- EventOne();
- }
- } CLEANUP {
- if (sicn) HandleRestore(sicn, sicnState);
- if (sound) HandleRestore(sound, soundState);
- if (posted) {
- NMRemove(¬ification);
- posted = false;
- }
- } ENDTRY;
- }
-
- /* Standard filter for alerts; handles command key equivalents for
- controls and draws the alert's icon. */
- Boolean AlertFilter(DialogPtr dlg, EventRecord *event, short *item,
- void *data)
- {
- short nitems; /* number of items in dialog */
- short click; /* item to click */
- char key; /* key pressed */
- CStr255 title; /* title of control */
- Handle icon; /* icon to draw */
- Rect box; /* icon's enclosing rectangle */
- GrafPtr port; /* saved port */
- Boolean result = false;
-
- /* setup port */
- GetPort(&port);
- SetPort(dlg);
-
- /* interpret command key combinations as clicks in controls */
- switch (event->what) {
- case keyDown:
- case autoKey:
- if (event->modifiers & cmdKey) {
-
- /* search for control whose first character matches the key pressed */
- key = event->message;
- nitems = DlgNItems(dlg);
- for (click = 1; ! result && click <= nitems; click++) {
-
- /* make sure item is an enabled control */
- if (DlgTypeCtl(DlgType(dlg, click)) && DlgCtlEnabled(dlg, click)) {
- Str31 keystr;
-
- /* compare first character of control's title to key */
- CtlTitle(DlgCtl(dlg, click), title);
- keystr[0] = 1; keystr[1] = key;
- UprString(keystr, true);
- UprString(c2pstr(title), true);
- if (keystr[1] == title[1]) {
-
- /* simulate a click on the control */
- *item = click;
- result = true;
- event->what = nullEvent;
- DlgFlashButton(dlg, click);
- }
- }
- }
- }
- break;
- case updateEvt:
- /* draw the icon */
- if (WinExtraPtr(dlg)->icon != ALERT_NO_ICON) {
- SetRect(&box, 20, 10, 52, 42);
- icon = GetIcon(WinExtraPtr(dlg)->icon);
- if (icon)
- PlotIcon(&box, icon);
- }
- break;
- }
- SetPort(port);
- return(result);
- }
-
- /* Display an alert using the 'ALRT' template with ID 'id', and display
- the icon with ID 'iconid'. The 'filter' parameter can be NULL, in
- which case the standard filter AlertFilter will be called, otherwise it
- should point to a modal dialog filter. The 'data' parameter will be passed
- to the filter function. The 'nstrings' parameter indicates the
- number of strings in the alert. Subsequent parameters will set static text
- items to the strings: the first static text item will be set to the first
- string, the second static text item will be set to the second string, etc.
- The item used to close the alert is returned. */
- short AlertCustom(short id, short iconid, DlgModalFilterType filter,
- void *data, short nstrings, ...)
- {
- volatile DialogPtr dlg = NULL; /* the dialog */
- AlertTHndl alrt = NULL;/* alert's template */
- va_list ap; /* for getting string parameters */
- short i = 0; /* index to dialog's items for setting the strings */
- short show = 0; /* if true then alert is drawn */
- short sound = 0; /* number of beeps */
- short dflt = 0; /* default item */
- short shift = 0; /* number of bits to shift */
- short item = 0; /* item clicked in alert */
- StageList stages = 0; /* alert's stage list */
-
- TRY {
-
- /* reset alert stage if this isn't the same alert as the previous alert */
- if (id != GetANumber())
- ResetAlrtStage();
-
- /* set default values for stage list parameters */
- show = true;
- sound = 1;
- dflt = ok;
-
- /* Examine the alert's StageList. This is a really ugly set of masks and
- shifts which get the sound number, the default button number,
- and whether to display the alert for the current stage. */
- alrt = (AlertTHndl) ResGet('ALRT', id);
-
- /* each stage uses 4 bits */
- stages = (**alrt).stages;
- shift = 4 * GetAlrtStage();
-
- /* get the fields for the current stage by masking off all the
- other bits */
- sound = (stages & (0x03 << shift));
- show = (stages & (0x04 << shift));
- dflt = (stages & (0x08 << shift));
-
- /* shift the bit fields right so they start from 0 */
- sound = (((unsigned short) sound) >> (shift + 0));
- show = (((unsigned short) show) >> (shift + 2));
- dflt = (((unsigned short) dflt) >> (shift + 3)) + 1;
-
- /* only create the dialog if something would happen */
- if (show || sound) {
-
- /* start interacting with the user */
- EventInteractWithUser();
-
- /* use notification manager if the application is in the background */
- NotifyIfInBackground();
-
- /* set up the dialog and then run it */
- if (show) {
-
- /* create dialog */
- dlg = DlgBeginAlert(id);
-
- /* set static text items */
- ap = va_start(ap, nstrings);
- for (i = 1; i <= DlgNItems(dlg) && nstrings > 0; i++) {
- if (DlgTypeStatText(DlgType(dlg, i))) {
- DlgTextSet(dlg, i, va_arg(ap, char *));
- nstrings--;
- }
- }
- va_end(ap);
-
- /* setup dialog */
- if (! filter)
- filter = AlertFilter;
- WinExtraPtr(dlg)->icon = iconid;
- DlgModalFilterSet(dlg, filter, data);
- DlgDefaultSet(dlg, dflt);
- DlgPosition(dlg);
- WinShow(dlg);
-
- /* call sound routine immediately before running alert */
- if (sound && GetDABeeper())
- GetDABeeper()(sound);
-
- /* run dialog until an enabled item is hit */
- item = DlgModalRun(dlg);
- }
- else {
- /* not showing alert, so just call sound routine */
- if (sound && GetDABeeper())
- GetDABeeper()(sound);
- }
- }
- } CLEANUP {
- DlgEnd(dlg);
- } ENDTRY;
- ensure(show ? item > 0 : item == 0);
- return(item);
- }
-
- /* display a stop alert */
- short AlertStop(short id, CStr255 str)
- {
- return(AlertCustom(id, stopIcon, NULL, NULL, 1, str));
- }
-
- /* display a note alert */
- short AlertNote(short id, CStr255 str)
- {
- return(AlertCustom(id, noteIcon, NULL, NULL, 1, str));
- }
-
- /* display a caution alert */
- short AlertCaution(short id, CStr255 str)
- {
- return(AlertCustom(id, cautionIcon, NULL, NULL, 1, str));
- }
-